昨天有提到事前規劃,今天就進入開發流程!
創建模型和遷移檔案可以用指令 php artisan make:model Task -m
自動建立
定義欄位規範
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
use HasFactory;
protected $fillable = [
'title',
'description',
'completed',
];
protected $casts = [
'completed' => 'boolean'
];
}
在遷移檔案中添加任務的欄位
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->boolean('completed')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
執行遷移來建立資料表
指令 php artisan migrate
controller
回顧:第 4 天:控制器
前提摘要
這裡會依照第 24 天:項目重構與優化的方法做分工,主要就是把本來寫在控制器的內容區分成商業邏輯以及拜訪資料庫!
創建 controller
來處理任務的 CRUD 操作
指令 php artisan make:controller TaskController
<?php
namespace App\Http\Controllers;
use App\Http\Requests\TaskRequest;
use App\Models\Task;
use App\Services\TaskService;
use App\Transformers\TaskTransformer;
use \Illuminate\Http\JsonResponse;
class TaskController extends Controller
{
public function __construct(
protected readonly TaskService $service,
protected readonly TaskTransformer $transformer,
) {
}
/**
* 取得所有任務明細
*
* @return JsonResponse
*/
public function showTasks(): JsonResponse
{
$tasks = $this->service->showTasks();
return response()->json($this->transformer->transformTasks($tasks), 200);
}
/**
* 新增任務
*
* @param TaskRequest $request
* @return mixed JsonResponse
*/
public function storeTask(TaskRequest $request): JsonResponse
{
$validated = $request->validated();
$task = $this->service->storeTask($validated);
return response()->json($this->transformer->transformTask($task), 201);
}
/**
* 編輯任務
*
* @param TaskRequest $request
* @param Task $task
* @return JsonResponse
*/
public function updateTask(TaskRequest $request, Task $task): JsonResponse
{
$validated = $request->validated();
$task = $this->service->updateTask($task, $validated);
return response()->json($this->transformer->transformTask($task), 201);
}
/**
* 改變任務的完成欄位
*
* @param Task $task
* @param bool $complete
* @return void
*/
public function changeTaskComplete(Task $task, bool $complete): void
{
$this->service->changeTaskComplete($task, $complete);
}
/**
* 刪除任務
*
* @param Task $task
* @return void
*/
public function deleteTask(Task $task): void
{
$this->service->deleteTask($task);
}
}
創建 require
validate
指令 php artisan make:request TaskRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
use Symfony\Component\HttpFoundation\Response;
class TaskRequest extends FormRequest
{
/**
* 預設function,定義授權規則
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* 預設 function,定義驗證規則
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'title' => ['required', 'string', 'max:255'],
'description' => ['required', 'string'],
'completed' => ['required', 'boolean'],
];
}
/**
* 預設 function,可自行定義錯誤訊息
*
* @return string[]
*/
public function messages()
{
return [
'title.required' => '標題必填',
'description.required' => '內容描述必填',
'completed.required' => '完成欄位必填',
];
}
// 預設 function,驗證失敗時會執行,這邊自定義錯誤時會傳json
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(
response: response()->json([
'status' => 'error',
'message' => 'Validation failed',
'errors' => $validator->errors(),
], status: Response::HTTP_UNPROCESSABLE_ENTITY)
);
}
// 預設 function,授權失敗時會執行,這邊自定義錯誤時會傳json
protected function failedAuthorization()
{
throw new HttpResponseException(
response: response()->json([
'status' => 'error',
'message' => 'Authorization failed',
], status: Response::HTTP_UNAUTHORIZED)
);
}
}
創建 transform
轉成與前端約定好的架構
這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Transforms
然後新增一個新的檔案
<?php
namespace App\Transformers;
use App\Models\Task;
use Illuminate\Support\Collection;
/**
* TaskTransformer
* 詳細說明: 任務相關資料轉換回傳的處理
*/
class TaskTransformer
{
/**
* 把模型的時間轉成 Y-M-D
*
* @param Task $task
* @return array
*/
public function transformTask(Task $task): array
{
return [
'id' => $task->id,
'title' => $task->title,
'description' => $task->description,
'completed' => $task->completed,
'start_at' => $task->created_at?->format('Y-m-d'),
'update_at' => $task->updated_at?->format('Y-m-d'),
];
}
/**
* 把模型集合的時間轉成 Y-M-D
*
* @param Collection $tasks
* @return array
*/
public function transformTasks(Collection $tasks): array
{
return $tasks->map(fn(Task $task) => [
'id' => $task->id,
'title' => $task->title,
'description' => $task->description,
'completed' => $task->completed,
'created_at' => $task->created_at->format('Y-m-d'),
'updated_at' => $task->updated_at->format('Y-m-d'),
])->toArray();
}
}
service
做邏輯處理這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Services
然後新增一個新的檔案
<?php
namespace App\Services;
use App\Models\Task;
use App\Repositories\TaskRepository;
use Illuminate\Support\Collection as SupportCollection;
/**
* TaskService
* 詳細說明: 任務邏輯處理
*/
class TaskService
{
public function __construct(
protected readonly TaskRepository $repository,
) {
}
/**
* 取得所有任務明細的邏輯處理
* @return SupportCollection
*/
public function showTasks(): SupportCollection
{
return $this->repository->showTasks();
}
/**
* 新增任務的邏輯處理
*
* @param array $data
* @return Task
*/
public function storeTask(array $data): Task
{
return $this->repository->storeTask($data);
}
/**
* 編輯任務的邏輯處理
*
* @param Task $task
* @param array $data
* @return Task
*/
public function updateTask(Task $task, array $data): Task
{
return $this->repository->updateTask($task, $data);
}
/**
* 改變任務的完成欄位邏輯處理
*
* @param Task $task
* @param bool $complete
* @return void
*/
public function changeTaskComplete(Task $task, bool $complete): void
{
$this->repository->changeTaskComplete($task, $complete);
}
/**
* 刪除任務的邏輯處理
*
* @param Task $task
* @return void
*/
public function deleteTask(Task $task): void
{
$this->repository->deleteTask($task);
}
}
repository
和資料庫互動這裡無法下指令,所以直接在 app 下創建一個新的資料夾 Repositories
然後新增一個新的檔案
<?php
namespace App\Repositories;
use App\Models\Task;
use \Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as SupportCollection;
/**
* TaskRepository
* 詳細說明: 任務資料邏輯
*/
class TaskRepository
{
/**
* 取得所有任務明細的資料處理
* @return SupportCollection
*/
public function showTasks(): SupportCollection
{
return Task::query()->get();
}
/**
* 新增任務的資料處理
*
* @param array $data
* @return Task
*/
public function storeTask(array $data): Task
{
return Task::create($data);
}
/**
* 編輯任務的資料處理
*
* @param Task $task
* @param array $data
* @return Task
*/
public function updateTask(Task $task, array $data): Task
{
$task->update($data);
return $task;
}
/**
* 改變任務的完成欄位資料處理
*
* @param Task $task
* @param bool $complete
* @return void
*/
public function changeTaskComplete(Task $task, bool $complete): void
{
$task->completed = $complete;
$task->save();
}
/**
* 刪除任務的資料處理
*
* @param \App\Models\Task $task
* @return void
*/
public function deleteTask(Task $task): void
{
$task->delete();
}
}